﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Web;
using DotNetNuke.Common.Utilities;
using DotNetNuke.Entities.Portals;
using DotNetNuke.Services.FileSystem;
using DotNetNuke.Services.Localization;

namespace DotNetNuke.ContentLibrary.Components.Common
{
    public class FileSystem
    {

        private static string AddFile(int portalId, Stream inStream, string fileName, string contentType, long length, string folderName, bool closeInputStream, bool clearCache, bool synchronize)
        {

            var objFolderController = new FolderController();
            var objFileController = new FileController();
            string sourceFolderName = GetSubFolderPath(fileName, portalId);
            var folder = objFolderController.GetFolder(portalId, sourceFolderName, false);
            string sourceFileName = GetFileName(fileName);
            var intFileID = 0;
            var retValue = "";

            retValue += CheckValidFileName(fileName);
            if (retValue.Length > 0)
            {
                return retValue;
            }

            var extension = Path.GetExtension(fileName).Replace(".", "");
            if (string.IsNullOrEmpty(contentType))
            {
                contentType = GetContentType(extension);
            }

            //Add file to Database
            intFileID = objFileController.AddFile(portalId, sourceFileName, extension, length, Null.NullInteger, Null.NullInteger, contentType, folderName, folder.FolderID, clearCache);

            //Save file to File Storage
            if (folder.StorageLocation != FolderController.StorageLocationTypes.InsecureFileSystem | synchronize == false)
            {
                WriteStream(intFileID, inStream, fileName, folder.StorageLocation, closeInputStream);
            }

            //Update the FileData
            retValue += UpdateFileData(intFileID, folder.FolderID, portalId, sourceFileName, extension, contentType, length, folderName);

            if (folder.StorageLocation != FolderController.StorageLocationTypes.InsecureFileSystem)
            {
                //try and delete the Insecure file
                DeleteFile(fileName);
            }

            if (folder.StorageLocation != FolderController.StorageLocationTypes.SecureFileSystem)
            {
                //try and delete the Secure file
                if (!fileName.EndsWith(".template"))
                {
                    DeleteFile(fileName + glbProtectedExtension);
                }
            }

            return retValue;

        }

        		/// -----------------------------------------------------------------------------
		/// <summary>
		/// Adds a Folder
		/// </summary>
		/// <param name="PortalId">The Id of the Portal</param>
		/// <param name="relativePath">The relative path of the folder</param>
		/// <param name="StorageLocation">The type of storage location</param>
		/// <history>
		///     [cnurse]    04/26/2006  Created
		/// </history>
		/// -----------------------------------------------------------------------------
		private static int AddFolder(int PortalId, string relativePath, int StorageLocation)
		{

			DotNetNuke.Services.FileSystem.FolderController objFolderController = new DotNetNuke.Services.FileSystem.FolderController();
			bool isProtected = FileSystemUtils.DefaultProtectedFolders(relativePath);
			int FolderID = objFolderController.AddFolder(PortalId, relativePath, StorageLocation, isProtected, false);

			if (PortalId != Null.NullInteger) {
				//Set Folder Permissions to inherit from parent
				SetFolderPermissions(PortalId, FolderID, relativePath);
			}

			return FolderID;

		}

		/// -----------------------------------------------------------------------------
		/// <summary>
		/// Checks that the file name is valid
		/// </summary>
		/// <param name="strFileName">The name of the file</param>
		/// <history>
		///     [cnurse]    04/26/2006  Created
		/// </history>
		/// -----------------------------------------------------------------------------
		private static string CheckValidFileName(string strFileName)
		{
			string retValue = Null.NullString;

			// check if file extension is restricted
			string strExtension = Path.GetExtension(strFileName).Replace(".", "");
			string strWhiteList = DotNetNuke.Entities.Host.Host.FileExtensions.ToLowerInvariant;
			if (string.IsNullOrEmpty(strExtension) || ("," + strWhiteList + ",").IndexOf("," + strExtension.ToLowerInvariant + ",") == -1) {
				if ((HttpContext.Current != null)) {
					retValue = "<br>" + string.Format(Localization.GetString("RestrictedFileType"), strFileName, Strings.Replace(strWhiteList, ",", ", *."));
				} else {
					retValue = "RestrictedFileType";
				}
			}

			//Return
			return retValue;
		}

		/// -----------------------------------------------------------------------------
		/// <summary>
		/// Gets the filename for a file path
		/// </summary>
		/// <param name="filePath">The full name of the file</param>
		/// <history>
		///     [cnurse]    04/26/2006  Created
		/// </history>
		/// -----------------------------------------------------------------------------
		private static string GetFileName(string filePath)
		{
			return System.IO.Path.GetFileName(filePath).Replace(glbProtectedExtension, "");
		}

		private static void RemoveOrphanedFiles(FolderInfo folder, int PortalId)
		{
			DotNetNuke.Services.FileSystem.FileInfo objFile = default(DotNetNuke.Services.FileSystem.FileInfo);
			FileController objFileController = new FileController();

			if (folder.StorageLocation != FolderController.StorageLocationTypes.DatabaseSecure) {
				foreach ( objFile in GetFilesByFolder(PortalId, folder.FolderID)) {
					RemoveOrphanedFile(objFile, PortalId);
				}
			}
		}

		private static void RemoveOrphanedFile(DotNetNuke.Services.FileSystem.FileInfo objFile, int PortalId)
		{
			FileController objFileController = new FileController();

			string strFile = "";
			switch (objFile.StorageLocation) {
				case FolderController.StorageLocationTypes.InsecureFileSystem:
					strFile = objFile.PhysicalPath;
					break;
				case FolderController.StorageLocationTypes.SecureFileSystem:
					strFile = objFile.PhysicalPath + glbProtectedExtension;
					break;
			}

			if (!string.IsNullOrEmpty(strFile)) {
				if (!File.Exists(strFile)) {
					objFileController.DeleteFile(PortalId, objFile.FileName, objFile.FolderId, true);
				}
			}
		}

		/// -----------------------------------------------------------------------------
		/// <summary>
		/// Updates a File
		/// </summary>
		/// <param name="strSourceFile">The original File Name</param>
		/// <param name="strDestFile">The new File Name</param>
		/// <param name="isCopy">Flag determines whether file is to be be moved or copied</param>
		/// <remarks>
		/// </remarks>
		/// <history>
		/// 	[cnurse]	12/2/2004	Created
		/// </history>
		/// -----------------------------------------------------------------------------
		private static string UpdateFile(string strSourceFile, string strDestFile, int PortalId, bool isCopy, bool isNew, bool ClearCache)
		{

			string retValue = "";
			retValue += CheckValidFileName(strSourceFile) + " ";
			retValue += CheckValidFileName(strDestFile);
			if (retValue.Length > 1) {
				return retValue;
			}
			retValue = "";

			try {
				DotNetNuke.Services.FileSystem.FolderController objFolderController = new DotNetNuke.Services.FileSystem.FolderController();
				DotNetNuke.Services.FileSystem.FileController objFileController = new DotNetNuke.Services.FileSystem.FileController();
				string sourceFolderName = GetSubFolderPath(strSourceFile, PortalId);
				string sourceFileName = GetFileName(strSourceFile);
				FolderInfo sourceFolder = objFolderController.GetFolder(PortalId, sourceFolderName, false);

				string destFileName = GetFileName(strDestFile);
				string destFolderName = GetSubFolderPath(strDestFile, PortalId);

				DotNetNuke.Services.FileSystem.FileInfo file = default(DotNetNuke.Services.FileSystem.FileInfo);

				if ((sourceFolder != null)) {
					file = objFileController.GetFile(sourceFileName, PortalId, sourceFolder.FolderID);
					if ((file != null)) {
						//Get the source Content from wherever it is
						Stream sourceStream = (Stream)GetFileStream(file);

						if (isCopy) {
							//Add the new File
							AddFile(PortalId, sourceStream, strDestFile, "", file.Size, destFolderName, true, ClearCache);
						} else {
							//Move/Update existing file
							FolderInfo destinationFolder = objFolderController.GetFolder(PortalId, destFolderName, false);

							//Now move the file

							if ((destinationFolder != null)) {
								objFileController.UpdateFile(file.FileId, destFileName, file.Extension, file.Size, file.Width, file.Height, file.ContentType, destFolderName, destinationFolder.FolderID);

								//Write the content to the Destination
								WriteStream(file.FileId, sourceStream, strDestFile, destinationFolder.StorageLocation, true);

								//Now we need to clean up the original files
								if (sourceFolder.StorageLocation == FolderController.StorageLocationTypes.InsecureFileSystem) {
									//try and delete the Insecure file
									DeleteFile(strSourceFile);
								}
								if (sourceFolder.StorageLocation == FolderController.StorageLocationTypes.SecureFileSystem) {
									//try and delete the Secure file
									DeleteFile(strSourceFile + glbProtectedExtension);
								}
							}
						}
					}
				}

			} catch (Exception ex) {
				retValue = ex.Message;
			}

			return retValue;

		}

		private static string UpdateFileData(int fileID, int folderID, int PortalId, string fileName, string extension, string contentType, long length, string folderName)
		{
			string retvalue = "";
			try {
				DotNetNuke.Services.FileSystem.FileController objFileController = new DotNetNuke.Services.FileSystem.FileController();
				System.Drawing.Image imgImage = default(System.Drawing.Image);
				int imageWidth = 0;
				int imageHeight = 0;

				if (Convert.ToBoolean(Strings.InStr(1, glbImageFileTypes + ",", extension.ToLower + ","))) {
					try {
						DotNetNuke.Services.FileSystem.FileInfo objFile = objFileController.GetFileById(fileID, PortalId);
						Stream imageStream = GetFileStream(objFile);
						imgImage = Drawing.Image.FromStream(imageStream);
						imageHeight = imgImage.Height;
						imageWidth = imgImage.Width;
						imgImage.Dispose();
						imageStream.Close();
					} catch {
						// error loading image file
						contentType = "application/octet-stream";
					} finally {
						//Update the File info
						objFileController.UpdateFile(fileID, fileName, extension, length, imageWidth, imageHeight, contentType, folderName, folderID);
					}
				}
			} catch (Exception ex) {
				retvalue = ex.Message;
			}

			return retvalue;
		}

		/// -----------------------------------------------------------------------------
		/// <summary>
		/// Writes a Stream to the appropriate File Storage
		/// </summary>
		/// <param name="fileId">The Id of the File</param>
		/// <param name="inStream">The Input Stream</param>
		/// <param name="fileName">The name of the file</param>
		/// <param name="StorageLocation">The type of storage location</param>
		/// <param name="closeInputStream">A flag that dermines if the Input Stream should be closed.</param>
		/// <remarks>
		/// </remarks>
		/// <history>
		/// 	[cnurse]	04/27/2006	Created
		/// </history>
		/// -----------------------------------------------------------------------------

		private static void WriteStream(int fileId, Stream inStream, string fileName, int storageLocation, bool closeInputStream)
		{
			DotNetNuke.Services.FileSystem.FileController objFileController = new DotNetNuke.Services.FileSystem.FileController();

			// Buffer to read 2K bytes in chunk:
			byte[] arrData = new byte[2049];
			Stream outStream = null;
			switch (storageLocation) {
				case FolderController.StorageLocationTypes.DatabaseSecure:
					objFileController.ClearFileContent(fileId);
					outStream = new MemoryStream();
					break;
				case FolderController.StorageLocationTypes.SecureFileSystem:
					if (File.Exists(fileName + glbProtectedExtension) == true) {
						File.Delete(fileName + glbProtectedExtension);
					}
					outStream = new FileStream(fileName + glbProtectedExtension, FileMode.Create);
					break;
				case FolderController.StorageLocationTypes.InsecureFileSystem:
					if (File.Exists(fileName) == true) {
						File.Delete(fileName);
					}
					outStream = new FileStream(fileName, FileMode.Create);
					break;
			}

			try {
				// Total bytes to read:
				int intLength = 0;
				// Read the data in buffer
				intLength = inStream.Read(arrData, 0, arrData.Length);
				while (intLength > 0) {
					// Write the data to the current output stream.
					outStream.Write(arrData, 0, intLength);

					//Read the next chunk
					intLength = inStream.Read(arrData, 0, arrData.Length);
				}

				if (storageLocation == FolderController.StorageLocationTypes.DatabaseSecure) {
					outStream.Seek(0, SeekOrigin.Begin);
					objFileController.UpdateFileContent(fileId, outStream);
				}
			} catch (Exception ex) {
				LogException(ex);
			} finally {
				if ((inStream == null) == false & closeInputStream) {
					// Close the file.
					inStream.Close();
				}
				if ((outStream == null) == false) {
					// Close the file.
					outStream.Close();
				}
			}

		}

		private static void WriteStream(HttpResponse objResponse, Stream objStream)
		{
			// Buffer to read 10K bytes in chunk:
			byte[] bytBuffer = new byte[10001];

			// Length of the file:
			int intLength = 0;

			// Total bytes to read:
			long lngDataToRead = 0;

			try {
				// Total bytes to read:
				lngDataToRead = objStream.Length;

				//objResponse.ContentType = "application/octet-stream"

				// Read the bytes.
				while (lngDataToRead > 0) {
					// Verify that the client is connected.
					if (objResponse.IsClientConnected) {
						// Read the data in buffer
						intLength = objStream.Read(bytBuffer, 0, 10000);

						// Write the data to the current output stream.
						objResponse.OutputStream.Write(bytBuffer, 0, intLength);

						// Flush the data to the HTML output.
						objResponse.Flush();

						 // ERROR: Not supported in C#: ReDimStatement

						// Clear the buffer
						lngDataToRead = lngDataToRead - intLength;
					} else {
						//prevent infinite loop if user disconnects
						lngDataToRead = -1;
					}
				}

			} catch (Exception ex) {
				// Trap the error, if any.
				objResponse.Write("Error : " + ex.Message);
			} finally {
				if ((objStream == null) == false) {
					// Close the file.
					objStream.Close();
				}
			}
		}

		#endregion

		#region "Path Manipulation Methods"

		public static string AddTrailingSlash(string strSource)
		{
			if (!strSource.EndsWith("\\"))
				strSource = strSource + "\\";
			return strSource;
		}

		public static string RemoveTrailingSlash(string strSource)
		{
			if (string.IsNullOrEmpty(strSource))
				return "";
			if (Strings.Mid(strSource, Strings.Len(strSource), 1) == "\\" | Strings.Mid(strSource, Strings.Len(strSource), 1) == "/") {
				return strSource.Substring(0, Strings.Len(strSource) - 1);
			} else {
				return strSource;
			}
		}

		public static string StripFolderPath(string strOrigPath)
		{
			if (strOrigPath.IndexOf("\\") != -1) {
				return Regex.Replace(strOrigPath, "^0\\\\", "");
			} else {
				return Strings.Replace(strOrigPath, "0", "", 1, 1);
			}
		}

		public static string FormatFolderPath(string strFolderPath)
		{
			if (string.IsNullOrEmpty(strFolderPath)) {
				return "";
			} else {
				if (strFolderPath.EndsWith("/")) {
					return strFolderPath;
				} else {
					return strFolderPath + "/";
				}
			}
		}

		#endregion

		#region "Public Methods"

		public static void AddFile(string FileName, int PortalId, string Folder, string HomeDirectoryMapPath, string contentType)
		{
			string strFile = HomeDirectoryMapPath + Folder + FileName;

			// add file to Files table
			FileController objFiles = new FileController();
			System.IO.FileInfo finfo = new System.IO.FileInfo(strFile);
			FolderController objFolders = new FolderController();
			FolderInfo objFolder = objFolders.GetFolder(PortalId, Folder, false);
			DotNetNuke.Services.FileSystem.FileInfo objFile = default(DotNetNuke.Services.FileSystem.FileInfo);
			objFile = objFiles.GetFile(FileName, PortalId, objFolder.FolderID);
			if (objFile == null) {
				objFiles.AddFile(PortalId, FileName, finfo.Extension, finfo.Length, 0, 0, contentType, "", objFolder.FolderID, true);
			} else {
				objFiles.UpdateFile(objFile.FileId, objFile.FileName, finfo.Extension, finfo.Length, 0, 0, contentType, "", objFolder.FolderID);
			}
		}

		/// -----------------------------------------------------------------------------
		/// <summary>
		/// Adds a File
		/// </summary>
		/// <param name="strFile">The File Name</param>
		/// <param name="PortalId">The Id of the Portal</param>
		/// <param name="ClearCache">A flag that indicates whether the file cache should be cleared</param>
		/// <remarks>This method is called by the SynchonizeFolder method, when the file exists in the file system
		/// but not in the Database
		/// </remarks>
		/// <history>
		/// 	[cnurse]	12/2/2004	Created
		///     [cnurse]    04/26/2006  Updated to account for secure storage
		///     [cnurse]    04/07/2008  Made public
		/// </history>
		/// -----------------------------------------------------------------------------
		public static string AddFile(string strFile, int PortalId, bool ClearCache, FolderInfo folder)
		{

			string retValue = "";
			try {
				DotNetNuke.Services.FileSystem.FileController objFileController = new DotNetNuke.Services.FileSystem.FileController();
				System.IO.FileInfo fInfo = new System.IO.FileInfo(strFile);
				string sourceFolderName = GetSubFolderPath(strFile, PortalId);

				string sourceFileName = null;
				//Remove the resources extesnion if we are in a Secure Folder
				if (folder.StorageLocation == FolderController.StorageLocationTypes.SecureFileSystem) {
					sourceFileName = GetFileName(strFile);
				} else {
					sourceFileName = strFile;
				}

				DotNetNuke.Services.FileSystem.FileInfo file = objFileController.GetFile(sourceFileName, PortalId, folder.FolderID);

				if (file == null) {
					//Add the new File
					AddFile(PortalId, fInfo.OpenRead(), strFile, "", fInfo.Length, sourceFolderName, true, ClearCache, true);
				} else {
					if (file.Size != fInfo.Length) {
						//optimistic assumption for speed: update only if filesize has changed
						string extension = Path.GetExtension(strFile).Replace(".", "");
						UpdateFileData(file.FileId, folder.FolderID, PortalId, sourceFileName, extension, GetContentType(extension), fInfo.Length, sourceFolderName);
					}
				}
			} catch (Exception ex) {
				retValue = ex.Message;
			}

			return retValue;
		}

		/// -----------------------------------------------------------------------------
		/// <summary>
		/// Adds a Folder
		/// </summary>
		/// <param name="_PortalSettings">Portal Settings for the Portal</param>
		/// <param name="parentFolder">The Parent Folder Name</param>
		/// <param name="newFolder">The new Folder Name</param>
		/// <remarks>
		/// </remarks>
		/// <history>
		/// 	[cnurse]	12/4/2004	Created
		/// </history>
		/// -----------------------------------------------------------------------------
		public static void AddFolder(PortalSettings _PortalSettings, string parentFolder, string newFolder)
		{
			AddFolder(_PortalSettings, parentFolder, newFolder, FolderController.StorageLocationTypes.InsecureFileSystem);
		}

		/// -----------------------------------------------------------------------------
		/// <summary>
		/// Adds a Folder
		/// </summary>
		/// <param name="_PortalSettings">Portal Settings for the Portal</param>
		/// <param name="parentFolder">The Parent Folder Name</param>
		/// <param name="newFolder">The new Folder Name</param>
		/// <param name="StorageLocation">The Storage Location</param>
		/// <remarks>
		/// </remarks>
		/// <history>
		/// 	[cnurse]	12/4/2004	Created
		/// </history>
		/// -----------------------------------------------------------------------------

		public static void AddFolder(PortalSettings _PortalSettings, string parentFolder, string newFolder, int StorageLocation)
		{
			int PortalId = 0;
			string ParentFolderName = null;
			System.IO.DirectoryInfo dinfo = new System.IO.DirectoryInfo(parentFolder);
			System.IO.DirectoryInfo dinfoNew = default(System.IO.DirectoryInfo);

			if (_PortalSettings.ActiveTab.ParentId == _PortalSettings.SuperTabId) {
				PortalId = Null.NullInteger;
				ParentFolderName = Common.Globals.HostMapPath;
			} else {
				PortalId = _PortalSettings.PortalId;
				ParentFolderName = _PortalSettings.HomeDirectoryMapPath;
			}

			dinfoNew = new System.IO.DirectoryInfo(parentFolder + newFolder);
			if (!dinfoNew.Exists) {
				dinfoNew = dinfo.CreateSubdirectory(newFolder);
			}

			string FolderPath = dinfoNew.FullName.Substring(ParentFolderName.Length).Replace("\\", "/");

			//Persist in Database
			AddFolder(PortalId, FolderPath, StorageLocation);

		}

		/// -----------------------------------------------------------------------------
		/// <summary>
		/// Adds a File to a Zip File
		/// </summary>
		/// <history>
		/// 	[cnurse]	12/4/2004	Created
		/// </history>
		/// -----------------------------------------------------------------------------
		public static void AddToZip(ref ZipOutputStream ZipFile, string filePath, string fileName, string folder)
		{
			Crc32 crc = new Crc32();

			//Open File Stream
			FileStream fs = File.OpenRead(filePath);

			//Read file into byte array buffer
			byte[] buffer = null;
			 // ERROR: Not supported in C#: ReDimStatement

			fs.Read(buffer, 0, buffer.Length);

			//Create Zip Entry
			ZipEntry entry = new ZipEntry(Path.Combine(folder, fileName));
			entry.DateTime = DateTime.Now;
			entry.Size = fs.Length;
			fs.Close();
			crc.Reset();
			crc.Update(buffer);
			entry.Crc = crc.Value;

			//Compress file and add to Zip file
			ZipFile.PutNextEntry(entry);
			ZipFile.Write(buffer, 0, buffer.Length);

		}

		/// -----------------------------------------------------------------------------
		/// <summary>
		/// Trys to copy a file in the file system
		/// </summary>
		/// <param name="sourceFileName">The name of the source file</param>
		/// <param name="destFileName">The name of the destination file</param>
		/// <history>
		///     [cnurse]    06/27/2008  Created
		/// </history>
		/// -----------------------------------------------------------------------------
		public static void CopyFile(string sourceFileName, string destFileName)
		{
			if (File.Exists(destFileName)) {
				File.SetAttributes(destFileName, FileAttributes.Normal);
			}
			File.Copy(sourceFileName, destFileName, true);
		}

		/// -----------------------------------------------------------------------------
		/// <summary>
		/// Copies a File
		/// </summary>
		/// <param name="strSourceFile">The original File Name</param>
		/// <param name="strDestFile">The new File Name</param>
		/// <param name="settings">The Portal Settings for the Portal/Host Account</param>
		/// <remarks>
		/// </remarks>
		/// <history>
		/// 	[cnurse]	12/2/2004	Created
		/// </history>
		/// -----------------------------------------------------------------------------
		public static string CopyFile(string strSourceFile, string strDestFile, PortalSettings settings)
		{
			return UpdateFile(strSourceFile, strDestFile, GetFolderPortalId(settings), true, false, true);
		}

		/// -----------------------------------------------------------------------------
		/// <summary>
		/// This checks to see if the folder is a protected type of folder 
		/// </summary>
		/// <param name="folderPath">String</param>
		/// <returns>Boolean</returns>
		/// <remarks>
		/// </remarks>
		/// <history>
		/// 	[cpaterra]	4/7/2006	Created
		/// </history>
		/// -----------------------------------------------------------------------------
		public static bool DefaultProtectedFolders(string folderPath)
		{
			if (string.IsNullOrEmpty(folderPath) | folderPath.ToLower == "skins" | folderPath.ToLower == "containers" | folderPath.ToLower.StartsWith("skins/") == true | folderPath.ToLower.StartsWith("containers/") == true) {
				return true;
			} else {
				return false;
			}
		}

		/// -----------------------------------------------------------------------------
		/// <summary>
		/// Trys to delete a file from the file system
		/// </summary>
		/// <param name="strFileName">The name of the file</param>
		/// <history>
		///     [cnurse]    04/26/2006  Created
		/// </history>
		/// -----------------------------------------------------------------------------
		public static void DeleteFile(string strFileName)
		{
			if (File.Exists(strFileName)) {
				File.SetAttributes(strFileName, FileAttributes.Normal);
				File.Delete(strFileName);
			}
		}

		/// -----------------------------------------------------------------------------
		/// <summary>
		/// Deletes a file
		/// </summary>
		/// <param name="strSourceFile">The File to delete</param>
		/// <param name="settings">The Portal Settings for the Portal/Host Account</param>
		/// <remarks>
		/// </remarks>
		/// <history>
		/// 	[Jon Henning]	11/1/2004	Created
		///     [cnurse]        12/6/2004   delete file from db
		/// </history>
		/// -----------------------------------------------------------------------------
		public static string DeleteFile(string strSourceFile, PortalSettings settings)
		{
			return DeleteFile(strSourceFile, settings, true);
		}

		/// -----------------------------------------------------------------------------
		/// <summary>
		/// Deletes a file
		/// </summary>
		/// <param name="strSourceFile">The File to delete</param>
		/// <param name="settings">The Portal Settings for the Portal/Host Account</param>
		/// <remarks>
		/// </remarks>
		/// <history>
		/// 	[Jon Henning]	11/1/2004	Created
		///     [cnurse]        12/6/2004   delete file from db
		/// </history>
		/// -----------------------------------------------------------------------------
		public static string DeleteFile(string strSourceFile, PortalSettings settings, bool ClearCache)
		{
			string retValue = "";
			string folderName = GetSubFolderPath(strSourceFile, settings.PortalId);
			string fileName = GetFileName(strSourceFile);
			int PortalId = GetFolderPortalId(settings);

			FolderController objFolders = new FolderController();
			FolderInfo objFolder = objFolders.GetFolder(PortalId, folderName, false);

			if (FolderPermissionController.CanAdminFolder(objFolder)) {
				try {
					//try and delete the Insecure file
					DeleteFile(strSourceFile);

					//try and delete the Secure file
					DeleteFile(strSourceFile + glbProtectedExtension);

					//Remove file from DataBase
					DotNetNuke.Services.FileSystem.FileController objFileController = new DotNetNuke.Services.FileSystem.FileController();
					objFileController.DeleteFile(PortalId, fileName, objFolder.FolderID, ClearCache);

				} catch (IOException ioEx) {
					retValue += "<br>" + string.Format(Localization.GetString("FileInUse"), strSourceFile);
				} catch (Exception ex) {
					retValue = ex.Message;
				}
			// insufficient folder permission in the application
			} else {
				retValue += "<br>" + string.Format(Localization.GetString("InsufficientFolderPermission"), folderName);
			}
			return retValue;
		}

		/// -----------------------------------------------------------------------------
		/// <summary>
		/// Deletes a folder
		/// </summary>
		/// <param name="PortalId">The Id of the Portal</param>
		/// <param name="folder">The Directory Info object to delete</param>
		/// <param name="folderName">The Name of the folder relative to the Root of the Portal</param>
		/// <remarks>
		/// </remarks>
		/// <history>
		/// 	[cnurse]	12/4/2004	Created
		/// </history>
		/// -----------------------------------------------------------------------------

		public static void DeleteFolder(int PortalId, System.IO.DirectoryInfo folder, string folderName)
		{
			//Delete Folder
			folder.Delete(false);

			//Remove Folder from DataBase
			DotNetNuke.Services.FileSystem.FolderController objFolderController = new DotNetNuke.Services.FileSystem.FolderController();
			objFolderController.DeleteFolder(PortalId, folderName.Replace("\\", "/"));

		}

		/// -----------------------------------------------------------------------------
		/// <summary>
		/// Moved directly from FileManager code, probably should make extension lookup more generic
		/// </summary>
		/// <param name="FileLoc">File Location</param>
		/// <remarks>
		/// </remarks>
		/// <history>
		/// 	[Jon Henning]	11/1/2004	Created
		/// 	[Jon Henning]	1/4/2005	Fixed extension comparison, added content length header - DNN-386
		/// </history>
		/// -----------------------------------------------------------------------------
		public static void DownloadFile(string FileLoc)
		{
			System.IO.FileInfo objFile = new System.IO.FileInfo(FileLoc);
			System.Web.HttpResponse objResponse = System.Web.HttpContext.Current.Response;
			string filename = objFile.Name;
			if (HttpContext.Current.Request.UserAgent.IndexOf("; MSIE ") > 0) {
				filename = HttpUtility.UrlEncode(filename);
			}
			if (objFile.Exists) {
				objResponse.ClearContent();
				objResponse.ClearHeaders();
				objResponse.AppendHeader("content-disposition", "attachment; filename=\"" + filename + "\"");
				objResponse.AppendHeader("Content-Length", objFile.Length.ToString());

				objResponse.ContentType = GetContentType(objFile.Extension.Replace(".", ""));

				WriteFile(objFile.FullName);

				objResponse.Flush();
				objResponse.End();
			}
		}

		/// -----------------------------------------------------------------------------
		/// <summary>
		/// Streams a file to the output stream if the user has the proper permissions
		/// </summary>
		/// <param name="settings">Portal Settings</param>
		/// <param name="FileId">FileId identifying file in database</param>
		/// <param name="ClientCache">Cache file in client browser - true/false</param>
		/// <param name="ForceDownload">Force Download File dialog box - true/false</param>
		/// <history>
		/// </history>
		/// -----------------------------------------------------------------------------
		public static bool DownloadFile(PortalSettings settings, int FileId, bool ClientCache, bool ForceDownload)
		{

			return DownloadFile(GetFolderPortalId(settings), FileId, ClientCache, ForceDownload);

		}

		/// -----------------------------------------------------------------------------
		/// <summary>
		/// Streams a file to the output stream if the user has the proper permissions
		/// </summary>
		/// <param name="PortalId">The Id of the Portal to which the file belongs</param>
		/// <param name="FileId">FileId identifying file in database</param>
		/// <param name="ClientCache">Cache file in client browser - true/false</param>
		/// <param name="ForceDownload">Force Download File dialog box - true/false</param>
		/// <history>
		/// </history>
		/// -----------------------------------------------------------------------------
		public static bool DownloadFile(int PortalId, int FileId, bool ClientCache, bool ForceDownload)
		{
			bool blnDownload = false;

			// get file
			FileController objFiles = new FileController();
			DotNetNuke.Services.FileSystem.FileInfo objFile = objFiles.GetFileById(FileId, PortalId);
			if ((objFile != null)) {
				string filename = objFile.FileName;
				if (HttpContext.Current.Request.UserAgent.IndexOf("; MSIE ") > 0) {
					filename = HttpUtility.UrlEncode(filename);
				}

				FolderController objFolders = new FolderController();
				FolderInfo objFolder = objFolders.GetFolder(PortalId, objFile.Folder, false);

				// check folder view permissions
				if (FolderPermissionController.CanViewFolder(objFolder)) {
					// auto sync
					bool blnFileExists = true;
					if (Host.EnableFileAutoSync) {
						string strFile = "";
						switch (objFile.StorageLocation) {
							case FolderController.StorageLocationTypes.InsecureFileSystem:
								strFile = objFile.PhysicalPath;
								break;
							case FolderController.StorageLocationTypes.SecureFileSystem:
								strFile = objFile.PhysicalPath + glbProtectedExtension;
								break;
						}
						if (!string.IsNullOrEmpty(strFile)) {
							// synchronize file
							System.IO.FileInfo objFileInfo = new System.IO.FileInfo(strFile);
							if (objFileInfo.Exists) {
								if (objFile.Size != objFileInfo.Length) {
									objFile.Size = Convert.ToInt32(objFileInfo.Length);
									UpdateFileData(FileId, objFile.FolderId, PortalId, objFile.FileName, objFile.Extension, GetContentType(objFile.Extension), objFileInfo.Length, objFile.Folder);
								}
							// file does not exist
							} else {
								RemoveOrphanedFile(objFile, PortalId);
								blnFileExists = false;
							}
						}
					}

					// download file
					if (blnFileExists) {
						// save script timeout
						int scriptTimeOut = HttpContext.Current.Server.ScriptTimeout;

						// temporarily set script timeout to large value ( this value is only applicable when application is not running in Debug mode )
						HttpContext.Current.Server.ScriptTimeout = int.MaxValue;

						HttpResponse objResponse = HttpContext.Current.Response;

						objResponse.ClearContent();
						objResponse.ClearHeaders();

						// force download dialog
						if (ForceDownload) {
							objResponse.AppendHeader("content-disposition", "attachment; filename=\"" + filename + "\"");
						} else {
							//use proper file name when browser forces download because of file type (save as name should match file name)
							objResponse.AppendHeader("content-disposition", "inline; filename=\"" + filename + "\"");
						}
						objResponse.AppendHeader("Content-Length", objFile.Size.ToString());
						objResponse.ContentType = GetContentType(objFile.Extension.Replace(".", ""));

						//Stream the file to the response
						IO.Stream objStream = FileSystemUtils.GetFileStream(objFile);
						try {
							WriteStream(objResponse, objStream);
						} catch (Exception ex) {
							// Trap the error, if any.
							objResponse.Write("Error : " + ex.Message);
						} finally {
							if ((objStream == null) == false) {
								// Close the file.
								objStream.Close();
							}
						}

						objResponse.Flush();
						objResponse.End();

						// reset script timeout
						HttpContext.Current.Server.ScriptTimeout = scriptTimeOut;

						blnDownload = true;
					}
				}
			}

			return blnDownload;

		}

		/// -----------------------------------------------------------------------------
		/// <summary>
		/// gets the content type based on the extension
		/// </summary>
		/// <param name="extension">The extension</param>
		/// <remarks>
		/// </remarks>
		/// <history>
		/// 	[cnurse]	04/26/2006	Created
		/// </history>
		/// -----------------------------------------------------------------------------
		public static string GetContentType(string extension)
		{

			string contentType = null;

			switch (extension.ToLower) {
				case "txt":
					contentType = "text/plain";
					break;
				case "htm":
				case "html":
					contentType = "text/html";
					break;
				case "rtf":
					contentType = "text/richtext";
					break;
				case "jpg":
				case "jpeg":
					contentType = "image/jpeg";
					break;
				case "gif":
					contentType = "image/gif";
					break;
				case "bmp":
					contentType = "image/bmp";
					break;
				case "mpg":
				case "mpeg":
					contentType = "video/mpeg";
					break;
				case "avi":
					contentType = "video/avi";
					break;
				case "pdf":
					contentType = "application/pdf";
					break;
				case "doc":
				case "dot":
					contentType = "application/msword";
					break;
				case "csv":
				case "xls":
				case "xlt":
					contentType = "application/x-msexcel";
					break;
				default:
					contentType = "application/octet-stream";
					break;
			}

			return contentType;

		}

		public static byte[] GetFileContent(DotNetNuke.Services.FileSystem.FileInfo objFile)
		{

			Stream objStream = FileSystemUtils.GetFileStream(objFile);
			BinaryReader objBinaryReader = new BinaryReader(objStream);
			byte[] objContent = objBinaryReader.ReadBytes(Convert.ToInt32(objStream.Length));
			objBinaryReader.Close();

			return objContent;

		}

		public static Stream GetFileStream(DotNetNuke.Services.FileSystem.FileInfo objFile)
		{

			DotNetNuke.Services.FileSystem.FileController objFileController = new DotNetNuke.Services.FileSystem.FileController();
			Stream fileStream = null;

			switch (objFile.StorageLocation) {
				case FolderController.StorageLocationTypes.InsecureFileSystem:
					// read from file system 
					fileStream = new FileStream(objFile.PhysicalPath, FileMode.Open, FileAccess.Read, FileShare.Read);
					break;
				case FolderController.StorageLocationTypes.SecureFileSystem:
					// read from file system 
					fileStream = new FileStream(objFile.PhysicalPath + glbProtectedExtension, FileMode.Open, FileAccess.Read, FileShare.Read);
					break;
				case FolderController.StorageLocationTypes.DatabaseSecure:
					// read from database 
					fileStream = new MemoryStream(objFileController.GetFileContent(objFile.FileId, objFile.PortalId));
					break;
			}

			return fileStream;

		}

		public static ArrayList GetFilesByFolder(int PortalId, int folderId)
		{
			FileController objFileController = new FileController();
			return CBO.FillCollection(objFileController.GetFiles(PortalId, folderId), typeof(DotNetNuke.Services.FileSystem.FileInfo));
		}

		public static int GetFolderPortalId(PortalSettings settings)
		{
			int FolderPortalId = Null.NullInteger;
			bool isHost = Convert.ToBoolean((settings.ActiveTab.ParentId == settings.SuperTabId ? true : false));
			if (!isHost) {
				FolderPortalId = settings.PortalId;
			}
			return FolderPortalId;
		}

		/// -----------------------------------------------------------------------------
		/// <summary>
		/// Gets all the folders for a Portal
		/// </summary>
		/// <param name="PortalId">The Id of the Portal</param>
		/// <remarks>
		/// </remarks>
		/// <history>
		/// 	[cnurse]	04/22/2006	Created
		/// </history>
		/// -----------------------------------------------------------------------------
		public static ArrayList GetFolders(int PortalID)
		{
			FolderController objFolderController = new FolderController();
			ArrayList arrFolders = new ArrayList();
			foreach (KeyValuePair<string, FolderInfo> folderPair in objFolderController.GetFoldersSorted(PortalID)) {
				arrFolders.Add(folderPair.Value);
			}
			return arrFolders;
		}

		public static FolderInfo GetFolder(int PortalID, string FolderPath)
		{
			FolderController objFolderController = new FolderController();
			FolderInfo objFolder = objFolderController.GetFolder(PortalID, FolderPath, false);
			if (Host.EnableFileAutoSync) {
				// synchronize files in folder
				if ((objFolder != null)) {
					SynchronizeFolder(objFolder.PortalID, objFolder.PhysicalPath, objFolder.FolderPath, false, true, false);
				}
			}
			return objFolder;
		}

		/// -----------------------------------------------------------------------------
		/// <summary>
		/// Gets all the subFolders for a Parent
		/// </summary>
		/// <param name="PortalId">The Id of the Portal</param>
		/// <remarks>
		/// </remarks>
		/// <history>
		/// 	[cnurse]	04/22/2006	Created
		/// </history>
		/// -----------------------------------------------------------------------------
		public static ArrayList GetFoldersByParentFolder(int PortalId, string ParentFolder)
		{

			ArrayList folders = GetFolders(PortalId);
			ArrayList subFolders = new ArrayList();
			foreach (FolderInfo folder in folders) {
				string strfolderPath = folder.FolderPath;
				if (folder.FolderPath.IndexOf(ParentFolder) > -1 && folder.FolderPath != Null.NullString && folder.FolderPath != ParentFolder) {
					if (ParentFolder == Null.NullString) {
						if (strfolderPath.IndexOf("/") == strfolderPath.LastIndexOf("/")) {
							subFolders.Add(folder);
						}
					} else if (strfolderPath.StartsWith(ParentFolder)) {
						strfolderPath = strfolderPath.Substring(ParentFolder.Length + 1);
						if (strfolderPath.IndexOf("/") == strfolderPath.LastIndexOf("/")) {
							subFolders.Add(folder);
						}
					}
				}
			}

			return subFolders;

		}

		public static ArrayList GetFoldersByUser(int PortalID, bool IncludeSecure, bool IncludeDatabase, string Permissions)
		{
			FolderController objFolderController = new FolderController();
			ArrayList arrFolders = new ArrayList();

			//Get all the folders for the Portal
			bool parentAdd = true;
			string parentPath = Null.NullString;
			foreach (FolderInfo folder in objFolderController.GetFoldersSorted(PortalID).Values) {
				bool canAdd = true;
				if (!string.IsNullOrEmpty(parentPath)) {
					//Test if the "Parent" can be added
					if (folder.FolderPath.StartsWith(parentPath) && parentAdd == false) {
						canAdd = false;
					}
				}

				if (canAdd) {
					switch (folder.StorageLocation) {
						case FolderController.StorageLocationTypes.DatabaseSecure:
							canAdd = IncludeDatabase;
							break;
						case FolderController.StorageLocationTypes.SecureFileSystem:
							canAdd = IncludeSecure;
							break;
					}

					if (canAdd && PortalID > Null.NullInteger) {
						//Check Folder Permissions for Portal Folders
						canAdd = FolderPermissionController.HasFolderPermission(folder.FolderPermissions, Permissions);
					}

					if (canAdd) {
						arrFolders.Add(folder);
					}
					parentAdd = canAdd;
					parentPath = folder.FolderPath;
				}
			}

			return arrFolders;

		}

		/// -----------------------------------------------------------------------------
		/// <summary>
		/// Moves (Renames) a File
		/// </summary>
		/// <param name="strSourceFile">The original File Name</param>
		/// <param name="strDestFile">The new File Name</param>
		/// <param name="settings">The Portal Settings for the Portal/Host Account</param>
		/// <remarks>
		/// </remarks>
		/// <history>
		/// 	[cnurse]	12/2/2004	Created
		/// </history>
		/// -----------------------------------------------------------------------------
		public static string MoveFile(string strSourceFile, string strDestFile, PortalSettings settings)
		{
			return UpdateFile(strSourceFile, strDestFile, GetFolderPortalId(settings), false, false, true);
		}

		public static string ReadFile(string filePath)
		{
			StreamReader objStreamReader = default(StreamReader);
			objStreamReader = File.OpenText(filePath);
			string fileContent = objStreamReader.ReadToEnd;
			objStreamReader.Close();

			return fileContent;
		}

		public static void RemoveOrphanedFolders(int PortalId)
		{
			DotNetNuke.Services.FileSystem.FolderController objFolderController = new DotNetNuke.Services.FileSystem.FolderController();
			FolderInfo objFolder = default(FolderInfo);
			ArrayList arrFolders = GetFolders(PortalId);
			foreach ( objFolder in arrFolders) {
				if (objFolder.StorageLocation != FolderController.StorageLocationTypes.DatabaseSecure) {
					if (Directory.Exists(objFolder.PhysicalPath) == false) {
						RemoveOrphanedFiles(objFolder, PortalId);
						objFolderController.DeleteFolder(PortalId, objFolder.FolderPath);
					}
				}
			}
		}

		public static void SaveFile(string FullFileName, byte[] Buffer)
		{
			if (System.IO.File.Exists(FullFileName)) {
				System.IO.File.SetAttributes(FullFileName, FileAttributes.Normal);
			}
			FileStream fs = new FileStream(FullFileName, FileMode.Create, FileAccess.Write);
			fs.Write(Buffer, 0, Buffer.Length);
			fs.Close();
		}

		/// -----------------------------------------------------------------------------
		/// <summary>
		/// Assigns 1 or more attributes to a file
		/// </summary>
		/// <param name="FileLoc">File Location</param>
		/// <param name="FileAttributesOn">Pass in Attributes you wish to switch on (i.e. FileAttributes.Hidden + FileAttributes.ReadOnly)</param>
		/// <remarks>
		/// </remarks>
		/// <history>
		/// 	[Jon Henning]	11/1/2004	Created
		/// </history>
		/// -----------------------------------------------------------------------------
		public static void SetFileAttributes(string FileLoc, int FileAttributesOn)
		{
			System.IO.File.SetAttributes(FileLoc, (FileAttributes)FileAttributesOn);
		}

		/// -----------------------------------------------------------------------------
		/// <summary>
		/// Sets a Folders Permissions to the Administrator Role
		/// </summary>
		/// <param name="PortalId">The Id of the Portal</param>
		/// <param name="FolderId">The Id of the Folder</param>
		/// <param name="AdministratorRoleId">The Id of the Administrator Role</param>
		/// <param name="relativePath">The folder's Relative Path</param>
		/// <remarks>
		/// </remarks>
		/// <history>
		/// 	[cnurse]	12/4/2004	Created
		/// </history>
		/// -----------------------------------------------------------------------------
		public static void SetFolderPermissions(int PortalId, int FolderId, int AdministratorRoleId, string relativePath)
		{
			//Get the folder
			FolderInfo folder = new FolderController().GetFolderInfo(PortalId, FolderId);

			//Set Permissions
			foreach (PermissionInfo objPermission in PermissionController.GetPermissionsByFolder()) {
				FolderPermissionInfo folderPermission = new FolderPermissionInfo(objPermission);
				folderPermission.FolderID = FolderId;
				folderPermission.RoleID = AdministratorRoleId;
				folder.FolderPermissions.Add(folderPermission, true);
			}

			FolderPermissionController.SaveFolderPermissions(folder);
		}

		/// -----------------------------------------------------------------------------
		/// <summary>
		/// Sets a Folders Permissions the same as the Folders parent folder
		/// </summary>
		/// <param name="PortalId">The Id of the Portal</param>
		/// <param name="FolderId">The Id of the Folder</param>
		/// <param name="relativePath">The folder's Relative Path</param>
		/// <remarks>
		/// </remarks>
		/// <history>
		/// 	[cnurse]	08/01/2006	Created
		/// </history>
		/// -----------------------------------------------------------------------------
		public static void SetFolderPermissions(int PortalId, int FolderId, string relativePath)
		{
			if (!string.IsNullOrEmpty(relativePath)) {
				//Get the folder
				FolderInfo folder = new FolderController().GetFolderInfo(PortalId, FolderId);

				string parentFolderPath = relativePath.Substring(0, relativePath.Substring(0, relativePath.Length - 1).LastIndexOf("/") + 1);

				//Iterate parent permissions to see if permisison has already been added
				foreach (FolderPermissionInfo objPermission in FolderPermissionController.GetFolderPermissionsCollectionByFolder(PortalId, parentFolderPath)) {
					FolderPermissionInfo folderPermission = new FolderPermissionInfo(objPermission);
					folderPermission.FolderID = FolderId;
					folderPermission.RoleID = objPermission.RoleID;
					folderPermission.UserID = objPermission.UserID;
					folderPermission.AllowAccess = objPermission.AllowAccess;
					folder.FolderPermissions.Add(folderPermission, true);
				}

				FolderPermissionController.SaveFolderPermissions(folder);
			}
		}

		public static void SetFolderPermission(int PortalId, int FolderId, int PermissionId, int RoleId, string relativePath)
		{
			SetFolderPermission(PortalId, FolderId, PermissionId, RoleId, Null.NullInteger, relativePath);
		}

		/// -----------------------------------------------------------------------------
		/// <summary>
		/// Sets a Folder Permission
		/// </summary>
		/// <param name="PortalId">The Id of the Portal</param>
		/// <param name="FolderId">The Id of the Folder</param>
		/// <param name="PermissionId">The Id of the Permission</param>
		/// <param name="RoleId">The Id of the Role</param>
		/// <param name="relativePath">The folder's Relative Path</param>
		/// <remarks>
		/// </remarks>
		/// <history>
		/// 	[cnurse]	01/12/2005	Created
		/// </history>
		/// -----------------------------------------------------------------------------
		public static void SetFolderPermission(int PortalId, int FolderId, int PermissionId, int RoleId, int UserId, string relativePath)
		{
			FolderPermissionInfo objFolderPermissionInfo = default(FolderPermissionInfo);
			FolderController objController = new FolderController();
			FolderInfo folder = objController.GetFolderInfo(PortalId, FolderId);

			//Iterate current permissions to see if permisison has already been added
			foreach ( objFolderPermissionInfo in folder.FolderPermissions) {
				if (objFolderPermissionInfo.FolderID == FolderId & objFolderPermissionInfo.PermissionID == PermissionId & objFolderPermissionInfo.RoleID == RoleId & objFolderPermissionInfo.UserID == UserId & objFolderPermissionInfo.AllowAccess == true) {
					return;
				}
			}

			//Permission not found so Add
			objFolderPermissionInfo = new FolderPermissionInfo();
			objFolderPermissionInfo.FolderID = FolderId;
			objFolderPermissionInfo.PermissionID = PermissionId;
			objFolderPermissionInfo.RoleID = RoleId;
			objFolderPermissionInfo.UserID = UserId;
			objFolderPermissionInfo.AllowAccess = true;
			folder.FolderPermissions.Add(objFolderPermissionInfo, true);

			FolderPermissionController.SaveFolderPermissions(folder);
		}

		public static void Synchronize(int PortalId, int AdministratorRoleId, string HomeDirectory)
		{
			string PhysicalRoot = HomeDirectory;
			string VirtualRoot = "";

			//Call Synchronize Folder that recursively parses the subfolders
			SynchronizeFolder(PortalId, PhysicalRoot, VirtualRoot, true);

			//Invalidate Cache
			DataCache.ClearFolderCache(PortalId);
		}

		public static void SynchronizeFolder(int PortalId, string physicalPath, string relativePath, bool isRecursive)
		{
			SynchronizeFolder(PortalId, physicalPath, relativePath, isRecursive, true, true);
		}

		public static void SynchronizeFolder(int PortalId, string physicalPath, string relativePath, bool isRecursive, bool syncFiles, bool forceFolderSync)
		{
			FolderController objFolderController = new FolderController();
			int FolderId = 0;
			bool isInSync = true;

			// synchronize folder collection
			if (forceFolderSync == true & string.IsNullOrEmpty(relativePath)) {
				RemoveOrphanedFolders(PortalId);
			}

			//Attempt to get the folder
			FolderInfo folder = objFolderController.GetFolder(PortalId, relativePath, false);

			DirectoryInfo dirInfo = new DirectoryInfo(physicalPath);
			if (dirInfo.Exists) {
				// check to see if the folder exists in the db
				if (folder == null) {
					//Add Folder to database
					FolderId = AddFolder(PortalId, relativePath, FolderController.StorageLocationTypes.InsecureFileSystem);
					folder = objFolderController.GetFolder(PortalId, relativePath, true);
					isInSync = false;
				} else {
					//Check whether existing folder is in sync by comparing LastWriteTime of the physical folder with the LastUpdated value in the database
					//*NOTE: dirInfo.LastWriteTime is updated when files are added to or deleted from a directory - but NOT when existing files are overwritten ( this is a known Windows Operating System issue )
					isInSync = (dirInfo.LastWriteTime.ToString("yyyyMMddhhmmss") == folder.LastUpdated.ToString("yyyyMMddhhmmss"));
				}

				if ((folder != null)) {
					if (syncFiles == true & (isInSync == false | forceFolderSync == true)) {
						//Get Physical Files in this Folder and sync them
						string[] strFiles = Directory.GetFiles(physicalPath);
						foreach (string strFileName in strFiles) {
							//Add the File if it doesn't exist, Update it if the file size has changed
							AddFile(strFileName, PortalId, false, folder);
						}

						//Removed orphaned files
						RemoveOrphanedFiles(folder, PortalId);

						//Update the folder with the LastWriteTime of the directory
						folder.LastUpdated = dirInfo.LastWriteTime;
						objFolderController.UpdateFolder(folder);
					}

					//Get Physical Sub Folders (and synchronize recursively)
					if (isRecursive) {
						string[] strFolders = Directory.GetDirectories(physicalPath);
						foreach (string strFolder in strFolders) {
							DirectoryInfo dir = new DirectoryInfo(strFolder);
							string relPath = Null.NullString;
							if (string.IsNullOrEmpty(relativePath)) {
								relPath = dir.Name + "/";
							} else {
								relPath = relativePath;
								if (!relativePath.EndsWith("/")) {
									relPath = relPath + "/";
								}
								relPath = relPath + dir.Name + "/";
							}
							SynchronizeFolder(PortalId, strFolder, relPath, true, syncFiles, forceFolderSync);
						}
					}
				}
			// physical folder does not exist on file system
			} else {
				if ((folder != null)) {
					// folder exists in DB
					if (folder.StorageLocation != FolderController.StorageLocationTypes.DatabaseSecure) {
						// remove files and folder from DB
						RemoveOrphanedFiles(folder, PortalId);
						objFolderController.DeleteFolder(PortalId, relativePath.Replace("\\", "/"));
					}
				}
			}

		}

		/// -----------------------------------------------------------------------------
		/// <summary>
		/// Unzips a File
		/// </summary>
		/// <param name="fileName">The zip File Name</param>
		/// <param name="DestFolder">The folder where the file is extracted to</param>
		/// <param name="settings">The Portal Settings for the Portal/Host Account</param>
		/// <remarks>
		/// </remarks>
		/// <history>
		/// </history>
		/// -----------------------------------------------------------------------------
		public static string UnzipFile(string fileName, string DestFolder, PortalSettings settings)
		{

			int FolderPortalId = GetFolderPortalId(settings);
			bool isHost = Convert.ToBoolean((settings.ActiveTab.ParentId == settings.SuperTabId ? true : false));

			PortalController objPortalController = new PortalController();
			DotNetNuke.Services.FileSystem.FolderController objFolderController = new DotNetNuke.Services.FileSystem.FolderController();
			DotNetNuke.Services.FileSystem.FileController objFileController = new DotNetNuke.Services.FileSystem.FileController();
			string sourceFolderName = GetSubFolderPath(fileName, settings.PortalId);
			string sourceFileName = GetFileName(fileName);
			FolderInfo folder = objFolderController.GetFolder(FolderPortalId, sourceFolderName, false);
			DotNetNuke.Services.FileSystem.FileInfo file = objFileController.GetFile(sourceFileName, FolderPortalId, folder.FolderID);
			int storageLocation = folder.StorageLocation;
			ZipInputStream objZipInputStream = default(ZipInputStream);
			ZipEntry objZipEntry = default(ZipEntry);
			string strMessage = "";
			string strFileName = "";
			string strExtension = null;

			//Get the source Content from wherever it is

			//Create a Zip Input Stream
			try {
				objZipInputStream = new ZipInputStream(GetFileStream(file));
			} catch (Exception ex) {
				return ex.Message;
			}

			ArrayList sortedFolders = new ArrayList();

			objZipEntry = objZipInputStream.GetNextEntry;

			//iterate folders
			while ((objZipEntry != null)) {
				if (objZipEntry.IsDirectory) {
					try {
						sortedFolders.Add(objZipEntry.Name.ToString);
					} catch (Exception ex) {
						objZipInputStream.Close();
						return ex.Message;
					}
				}
				objZipEntry = objZipInputStream.GetNextEntry;
			}

			sortedFolders.Sort();

			foreach (string s in sortedFolders) {
				try {
					AddFolder(settings, DestFolder, s.ToString, storageLocation);
				} catch (Exception ex) {
					return ex.Message;
				}
			}

			//Recreate the Zip Input Stream and parse it for the files
			objZipInputStream = new ZipInputStream(GetFileStream(file));
			objZipEntry = objZipInputStream.GetNextEntry;
			while ((objZipEntry != null)) {
				if (!objZipEntry.IsDirectory) {
					if (objPortalController.HasSpaceAvailable(FolderPortalId, objZipEntry.Size)) {
						strFileName = Path.GetFileName(objZipEntry.Name);
						if (!string.IsNullOrEmpty(strFileName)) {
							strExtension = Path.GetExtension(strFileName).Replace(".", "");
							if (Strings.InStr(1, "," + Host.FileExtensions.ToLower, "," + strExtension.ToLower) != 0 | isHost) {
								try {
									string folderPath = System.IO.Path.GetDirectoryName(DestFolder + Strings.Replace(objZipEntry.Name, "/", "\\"));
									DirectoryInfo Dinfo = new DirectoryInfo(folderPath);
									if (!Dinfo.Exists) {
										AddFolder(settings, DestFolder, objZipEntry.Name.Substring(0, Strings.Replace(objZipEntry.Name, "/", "\\").LastIndexOf("\\")));
									}

									string zipEntryFileName = DestFolder + Strings.Replace(objZipEntry.Name, "/", "\\");
									strMessage += AddFile(FolderPortalId, objZipInputStream, zipEntryFileName, "", objZipEntry.Size, GetSubFolderPath(zipEntryFileName, settings.PortalId), false, false);
								} catch (Exception ex) {
									if ((objZipInputStream != null)) {
										objZipInputStream.Close();
									}
									return ex.Message;
								}
							} else {
								// restricted file type
								strMessage += "<br>" + string.Format(Localization.GetString("RestrictedFileType"), strFileName, Strings.Replace(Host.FileExtensions, ",", ", *."));
							}
						}
					// file too large
					} else {
						strMessage += "<br>" + string.Format(Localization.GetString("DiskSpaceExceeded"), strFileName);
					}

				}

				objZipEntry = objZipInputStream.GetNextEntry;
			}

			objZipInputStream.Close();

			return strMessage;

		}

		public static void UnzipResources(ZipInputStream zipStream, string destPath)
		{
			ZipEntry objZipEntry = default(ZipEntry);
			string LocalFileName = null;
			string RelativeDir = null;
			string FileNamePath = null;

			objZipEntry = zipStream.GetNextEntry;
			while ((objZipEntry != null)) {
				// This gets the Zipped FileName (including the path)
				LocalFileName = objZipEntry.Name;

				// This creates the necessary directories if they don't 
				// already exist.
				RelativeDir = Path.GetDirectoryName(objZipEntry.Name);
				if ((RelativeDir != string.Empty) && (!Directory.Exists(Path.Combine(destPath, RelativeDir)))) {
					Directory.CreateDirectory(Path.Combine(destPath, RelativeDir));
				}

				// This block creates the file using buffered reads from the zipfile
				if ((!objZipEntry.IsDirectory) && (!string.IsNullOrEmpty(LocalFileName))) {
					FileNamePath = Path.Combine(destPath, LocalFileName).Replace("/", "\\");

					try {
						// delete the file if it already exists
						if (File.Exists(FileNamePath)) {
							File.SetAttributes(FileNamePath, FileAttributes.Normal);
							File.Delete(FileNamePath);
						}

						// create the file
						FileStream objFileStream = File.Create(FileNamePath);

						int intSize = 2048;
						byte[] arrData = new byte[2049];

						intSize = zipStream.Read(arrData, 0, arrData.Length);
						while (intSize > 0) {
							objFileStream.Write(arrData, 0, intSize);
							intSize = zipStream.Read(arrData, 0, arrData.Length);
						}

						objFileStream.Close();
					} catch {
						// an error occurred saving a file in the resource file
					}

				}

				objZipEntry = zipStream.GetNextEntry;
			}
			if ((zipStream != null)) {
				zipStream.Close();
			}

		}

		/// <summary>
		/// UploadFile pocesses a single file 
		/// </summary>
		/// <param name="RootPath">The folder wherr the file will be put</param>
		/// <param name="objHtmlInputFile">The file to upload</param>
		/// <param name="Unzip"></param> 
		public static string UploadFile(string RootPath, HttpPostedFile objHtmlInputFile, bool Unzip = false)
		{
			return UploadFile(RootPath, objHtmlInputFile, Null.NullString, Unzip);
		}

		/// -----------------------------------------------------------------------------
		/// <summary>
		/// UploadFile processes a single file 
		/// </summary>
		/// <param name="RootPath">The folder wherr the file will be put</param>
		/// <param name="objHtmlInputFile">The file to upload</param>
		/// <param name="NewFileName"></param>
		/// <param name="Unzip"></param> 
		/// <remarks>
		/// </remarks>
		/// <history>
		///     [cnurse]        16/9/2004   Updated for localization, Help and 508
		///     [Philip Beadle] 10/06/2004  Moved to Globals from WebUpload.ascx.vb so can be accessed by URLControl.ascx
		///     [cnurse]        04/26/2006  Updated for Secure Storage
		///     [sleupold]      08/14/2007  Added NewFileName
		/// </history>
		/// -----------------------------------------------------------------------------
		public static string UploadFile(string RootPath, HttpPostedFile objHtmlInputFile, string NewFileName, bool Unzip = false)
		{

			// Obtain PortalSettings from Current Context
			PortalSettings settings = PortalController.GetCurrentPortalSettings;
			int PortalId = GetFolderPortalId(settings);
			bool isHost = Convert.ToBoolean((settings.ActiveTab.ParentId == settings.SuperTabId ? true : false));

			PortalController objPortalController = new PortalController();
			string strMessage = "";
			string strFileName = objHtmlInputFile.FileName;
			if (NewFileName != Null.NullString)
				strFileName = NewFileName;
			strFileName = RootPath + Path.GetFileName(strFileName);
			string strExtension = Strings.Replace(Path.GetExtension(strFileName), ".", "");
			string strFolderpath = GetSubFolderPath(strFileName, PortalId);

			FolderController objFolders = new FolderController();
			FolderInfo objFolder = objFolders.GetFolder(PortalId, strFolderpath, false);

			if (FolderPermissionController.CanAdminFolder(objFolder)) {
				if (objPortalController.HasSpaceAvailable(PortalId, objHtmlInputFile.ContentLength)) {
					if (Strings.InStr(1, "," + Host.FileExtensions.ToUpper, "," + strExtension.ToUpper) != 0 | isHost) {
						//Save Uploaded file to server
						try {
							strMessage += AddFile(PortalId, objHtmlInputFile.InputStream, strFileName, objHtmlInputFile.ContentType, objHtmlInputFile.ContentLength, strFolderpath, true, true);

							//Optionally Unzip File?
							if (Path.GetExtension(strFileName).ToLower == ".zip" & Unzip == true) {
								strMessage += UnzipFile(strFileName, RootPath, settings);
							}
						} catch (Exception Exc) {
							// save error - can happen if the security settings are incorrect on the disk
							strMessage += "<br>" + string.Format(Localization.GetString("SaveFileError"), strFileName);
						}
					} else {
						// restricted file type
						strMessage += "<br>" + string.Format(Localization.GetString("RestrictedFileType"), strFileName, Strings.Replace(Host.FileExtensions, ",", ", *."));
					}
				// file too large
				} else {
					strMessage += "<br>" + string.Format(Localization.GetString("DiskSpaceExceeded"), strFileName);
				}
			// insufficient folder permission in the application
			} else {
				strMessage += "<br>" + string.Format(Localization.GetString("InsufficientFolderPermission"), strFolderpath);
			}

			return strMessage;
		}

		#region "Telerik Integration"

		/// <summary>
		/// 
		/// </summary>
		/// <param name="RootPath"></param>
		/// <param name="objTelerikFile"></param>
		/// <param name="Unzip"></param>
		/// <returns></returns>
		/// <remarks></remarks>
		public static string UploadTelerikFile(string RootPath, Telerik.Web.UI.UploadedFile objTelerikFile, bool Unzip = false)
		{
			return UploadTelerikFile(RootPath, objTelerikFile, Null.NullString, Unzip);
		}

		/// <summary>
		/// UploadFile processes a single file from a Telerik upload control. 
		/// </summary>
		/// <param name="RootPath">The folder the file will be uploaded to.</param>
		/// <param name="objTelerikFile">The file being uploaded.</param>
		/// <param name="NewFileName">Empty if the file should not be renamed, otherwise the upload will be renamed.</param>
		/// <param name="Unzip">If the file should be unzipped or not, only applicable if it is zipped.</param>
		/// <returns>Nothing if completed without error, else a message stating the error.</returns>
		/// <remarks>Copy of Upload File with parameter change for Telerik instead of HttpPostedFile.</remarks>
		public static string UploadTelerikFile(string RootPath, Telerik.Web.UI.UploadedFile objTelerikFile, string NewFileName, bool Unzip = false)
		{

			// Obtain PortalSettings from Current Context
			PortalSettings settings = PortalController.GetCurrentPortalSettings;
			int PortalId = GetFolderPortalId(settings);
			bool isHost = Convert.ToBoolean((settings.ActiveTab.ParentId == settings.SuperTabId ? true : false));

			PortalController objPortalController = new PortalController();
			string strMessage = "";
			string strFileName = objTelerikFile.FileName;
			if (NewFileName != Null.NullString)
				strFileName = NewFileName;
			strFileName = RootPath + Path.GetFileName(strFileName);
			string strExtension = Strings.Replace(Path.GetExtension(strFileName), ".", "");
			string strFolderpath = GetSubFolderPath(strFileName, PortalId);

			FolderController objFolders = new FolderController();
			FolderInfo objFolder = objFolders.GetFolder(PortalId, strFolderpath, false);

			if (FolderPermissionController.CanAdminFolder(objFolder)) {
				if (objPortalController.HasSpaceAvailable(PortalId, objTelerikFile.ContentLength)) {
					if (Strings.InStr(1, "," + Host.FileExtensions.ToUpper, "," + strExtension.ToUpper) != 0 | isHost) {
						//Save Uploaded file to server
						try {
							strMessage += AddFile(PortalId, objTelerikFile.InputStream, strFileName, objTelerikFile.ContentType, objTelerikFile.ContentLength, strFolderpath, true, true);

							//Optionally Unzip File?
							if (Path.GetExtension(strFileName).ToLower == ".zip" & Unzip == true) {
								strMessage += UnzipFile(strFileName, RootPath, settings);
							}
						} catch (Exception Exc) {
							// save error - can happen if the security settings are incorrect on the disk
							strMessage += "<br>" + string.Format(Localization.GetString("SaveFileError"), strFileName);
						}
					} else {
						// restricted file type
						strMessage += "<br>" + string.Format(Localization.GetString("RestrictedFileType"), strFileName, Strings.Replace(Host.FileExtensions, ",", ", *."));
					}
				// file too large
				} else {
					strMessage += "<br>" + string.Format(Localization.GetString("DiskSpaceExceeded"), strFileName);
				}
			// insufficient folder permission in the application
			} else {
				strMessage += "<br>" + string.Format(Localization.GetString("InsufficientFolderPermission"), strFolderpath);
			}

			return strMessage;
		}

		#endregion

		public static void WriteFile(string strFileName)
		{
			System.Web.HttpResponse objResponse = System.Web.HttpContext.Current.Response;
			System.IO.Stream objStream = null;

			try {
				// Open the file.
				objStream = new System.IO.FileStream(strFileName, System.IO.FileMode.Open, IO.FileAccess.Read, IO.FileShare.Read);

				WriteStream(objResponse, objStream);
			} catch (Exception ex) {
				// Trap the error, if any.
				objResponse.Write("Error : " + ex.Message);
			} finally {
				if ((objStream == null) == false) {
					// Close the file.
					objStream.Close();
				}
			}
		}

		public static string DeleteFiles(Array arrPaths)
		{
			string strPath = null;
			string strExceptions = "";

			foreach ( strPath in arrPaths) {
				// remove comment
				if (strPath.IndexOf("'") != -1) {
					strPath = strPath.Substring(0, strPath.IndexOf("'"));
				}

				if (!string.IsNullOrEmpty(strPath.Trim)) {
					strPath = Common.Globals.ApplicationMapPath + "\\" + strPath;
					if (strPath.EndsWith("\\")) {
						// folder
						if (Directory.Exists(strPath)) {
							// delete the folder
							try {
								DotNetNuke.Common.DeleteFolderRecursive(strPath);
							} catch (Exception ex) {
								strExceptions += "Error: " + ex.Message + Constants.vbCrLf;
							}
						}
					} else {
						// file
						if (File.Exists(strPath)) {
							// delete the file
							try {
								File.SetAttributes(strPath, FileAttributes.Normal);
								File.Delete(strPath);
							} catch (Exception ex) {
								strExceptions += "Error: " + ex.Message + Constants.vbCrLf;
							}
						}
					}
				}
			}

			return strExceptions;
		}

		public static string SendFile(string URL, string FilePath)
		{
			var strMessage = "";
			try {
				var objWebClient = new WebClient();
				var responseArray = objWebClient.UploadFile(URL, "POST", FilePath);
			} catch (Exception ex) {
				strMessage = ex.Message;
			}
			return strMessage;
		}

		public static string ReceiveFile(HttpRequest Request, string FolderPath)
		{
			string strMessage = "";
			try {
				if (Request.Files.AllKeys.Length != 0) {
					string strKey = Request.Files.AllKeys(0);
					HttpPostedFile objFile = Request.Files(strKey);
					objFile.SaveAs(FolderPath + objFile.FileName);
				}
			} catch (Exception ex) {
				strMessage = ex.Message;
			}
			return strMessage;
		}

		public static string PullFile(string url, string filePath)
		{
			var strMessage = "";
			try {
				var objWebClient = new WebClient();
				objWebClient.DownloadFile(url, filePath);
			} catch (Exception ex) {
				strMessage = ex.Message;
			}
			return strMessage;
		}

		#endregion

	}

}